<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>
      
    线程编程指导 第一章 - Arisu
    
    </title>
    <link rel="shortcut icon" href="https://ushio.oss-cn-shanghai.aliyuncs.com/Arisu/Arisu_Icon.png" type="image/png" />

    
    
    <link href="atom.xml" rel="alternate" title="Arisu" type="application/atom+xml">
    <link rel="stylesheet" href="asset/css/style.min.css">
    <link rel="stylesheet" href="asset/css/doc.css">
    <script src="asset/app.js"></script>
</head>
  <body>
    <section class="hero">
      <div class="hero-head">
          <nav class="navbar" role="navigation" aria-label="main navigation">
              <div class="container">
              <div class="navbar-brand">
                
                <a target="_self" class="navbar-item " href="index.html">主页</a>
                
                <a target="_self" class="navbar-item " href="Apple.html">Apple</a>
                

                <a role="button" id="navbarSNSRssSwitchBtn" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="navbarSNSRssButtons">
                  <span aria-hidden="true"></span>
                  <span aria-hidden="true"></span>
                  <span aria-hidden="true"></span>
                </a>
              </div>
            
              <div id="navbarSNSRssButtons" class="navbar-menu">
                <div class="navbar-start">
                  
                </div>
            
                <div class="navbar-end">
                  <div class="navbar-item">
                    <!--buttons start-->
                    <div class="buttons">
                      
                        
                        
                        
                        
                      
                      <a href="atom.xml" target="_blank" title="RSS">
                          <span class="icon is-large has-text-black-bis">
                              <svg class="svg-inline--fa fa-rss fa-w-14 fa-lg" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="rss" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg=""><path fill="currentColor" d="M128.081 415.959c0 35.369-28.672 64.041-64.041 64.041S0 451.328 0 415.959s28.672-64.041 64.041-64.041 64.04 28.673 64.04 64.041zm175.66 47.25c-8.354-154.6-132.185-278.587-286.95-286.95C7.656 175.765 0 183.105 0 192.253v48.069c0 8.415 6.49 15.472 14.887 16.018 111.832 7.284 201.473 96.702 208.772 208.772.547 8.397 7.604 14.887 16.018 14.887h48.069c9.149.001 16.489-7.655 15.995-16.79zm144.249.288C439.596 229.677 251.465 40.445 16.503 32.01 7.473 31.686 0 38.981 0 48.016v48.068c0 8.625 6.835 15.645 15.453 15.999 191.179 7.839 344.627 161.316 352.465 352.465.353 8.618 7.373 15.453 15.999 15.453h48.068c9.034-.001 16.329-7.474 16.005-16.504z"></path></svg><!-- <i class="fas fa-rss fa-lg"></i> -->
                          </span>
                      </a>
                    </div>
                    <!--buttons end-->

                  </div>
                </div>
                </div>
              </div>
            </nav>
      </div>

 <div class="hero-body ct-body"></div>
      
    </section>
    <section class="ct-body">
      <div class="container">
          <div class="columns is-variable bd-klmn-columns is-4 is-centered">
              <div class="column is-four-fifths">
                  <div class="post-body single-content">
                    
                    <h1 class="title">
                            线程编程指导 第一章   
                      </h1>
                     
                    
                      <div class="media">
                            
                            <figure class="media-left">
                              <p class="image is-48x48">
                                
                                  <img class="is-rounded" src="https://ushio.oss-cn-shanghai.aliyuncs.com/Arisu/Arisu_Icon.png">
                                
                              </p>
                            </figure>
                            
                            <div class="media-content">
                              <div class="content">
                                <p>
                                 <span class="date">2020/02/25</span>
                                  <span class="tran-posted-in">posted in</span>&nbsp; 
                                  
                                      <span class="posted-in"><a href='Apple%20Developer%20Documentation.html'>Apple Developer Documentation</a></span>
                                  
                                      <span class="posted-in"><a href='%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B%E6%8C%87%E5%AF%BC.html'>线程编程指导</a></span>
                                         
                                  

                                   
                                      
                                  <br />
                                  <span class="tran-tags">Tags:</span>&nbsp;
                                  
                                    <a class="tag is-link is-light" href='tag_%E6%8A%80%E6%9C%AF%E6%96%87%E6%A1%A3.html'>#技术文档</a>
                                  
                                    <a class="tag is-link is-light" href='tag_Arisu.html'>#Arisu</a>
                                     

                                </p>
                              </div>
                            </div>
                         
                    </div>
                </div>
                  <article class="markdown-body single-content">
                    <h1><a id="%E5%85%B3%E4%BA%8E%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>关于线程编程</h1>
<p>多年来，最大的计算机性能主要受到计算机核心单个微处理器速度的限制。然而，随着各个处理器的速度开始达到其实际限制，芯片制造商转而采用多核设计，使计算机有机会同时执行多项任务。虽然 OS X 可以利用这些核心来执行与系统相关的任务，但您自己的应用程序也可以通过线程利用它们。</p>
<h2><a id="%E4%BB%80%E4%B9%88%E6%98%AF%E7%BA%BF%E7%A8%8B%EF%BC%9F" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>什么是线程？</h2>
<p><u>线程是在应用程序内部实现多个执行路径的相对轻量级方式。</u>在系统级，程序并行运行，系统根据需要和其他程序的需要，将执行时间分配给每个程序。然而，在每个程序内部存在一个或多个执行线程，其可用于同时或以几乎同时的方式执行不同的任务。系统本身实际上管理这些执行线程，将它们安排在可用内核上运行，并根据需要抢先中断它们以允许其他线程运行。</p>
<p>从技术角度来看，线程是管理代码执行所需的内核级和应用级数据结构的组合。内核级结构协调事件到线程的调度以及在一个可用内核上的线程的抢占式调度。应用程序级结构包括用于存储函数调用的调用堆栈以及应用程序管理和操作线程属性和状态所需的结构。</p>
<p>在非并发应用程序中，只有一个执行线程。该线程以应用程序的主例程开始和结束，并逐个分支到不同的方法或函数，以实现应用程序的整体行为。相比之下，支持并发的应用程序从一个线程开始，并根据需要添加更多以创建其他执行路径。每个新路径都有自己的自定义启动例程，该例程独立于应用程序主例程中的代码运行。<u>在应用程序中拥有多个线程提供了两个非常重要的潜在优势：</u></p>
<ul>
<li>多线程可以提高应用程序的感知响应能力。</li>
<li>多线程可以提高应用程序在多核系统上的实时性能。</li>
</ul>
<p>如果您的应用程序只有一个线程，那么一个线程必须执行所有操作。它必须响应事件，更新应用程序的窗口，并执行实现应用程序行为所需的所有计算。只有一个线程的问题是它一次只能做一件事。那么当你的一个计算需要很长时间才能完成时会发生什么？当您的代码忙于计算所需的值时，您的应用程序将停止响应用户事件并更新其窗口。如果此行为持续足够长的时间，用户可能会认为您的应用程序已挂起并尝试强制退出它。但是，如果将自定义计算移动到单独的线程上，则应用程序的主线程可以更及时地响应用户交互。</p>
<p>对于目前常见的多核计算机，线程提供了一种提高某些类型应用程序性能的方法。执行不同任务的线程可以在不同的处理器内核上同时执行，从而使应用程序可以在给定的时间内增加它的工作量。</p>
<p>当然，线程不是解决应用程序性能问题的灵丹妙药。除了线程提供的好处之外，还存在潜在的问题。在应用程序中具有多个执行路径会给代码增加相当大的复杂性。每个线程都必须与其他线程协调其操作，以防止它破坏应用程序的状态信息。由于单个应用程序中的线程共享相同的内存空间，因此它们可以访问所有相同的数据结构。如果两个线程试图同时操作相同的数据结构，则一个线程可能以破坏结果数据结构的方式覆盖另一个线程的更改。即使有适当的保护措施，您仍然需要注意编译器优化，这些优化会在代码中引入细微（而不是那么微妙）的错误。</p>
<h2><a id="%E7%BA%BF%E7%A8%8B%E6%9C%AF%E8%AF%AD" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>线程术语</h2>
<p>在深入讨论线程及其支持技术之前，有必要定义一些基本术语。</p>
<p>如果您熟悉 UNIX 系统，则可能会发现本文档对“任务”一词的使用方式不同。在 UNIX 系统上，术语“任务”有时用于指代正在运行的进程。</p>
<p>本文件采用以下术语：</p>
<ul>
<li>术语“线程”用于指代代码的单独执行路径。</li>
<li>术语“进程”用于指代正在运行的可执行文件，其可以包含多个线程。</li>
<li>术语“任务”用于指代需要执行的抽象工作概念。</li>
</ul>
<h2><a id="%E7%BA%BF%E7%A8%8B%E7%9A%84%E6%9B%BF%E4%BB%A3%E5%93%81" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>线程的替代品</h2>
<p>自己创建线程的一个问题是它们会给代码增加不确定性。线程是一种支持应用程序并发性的相对低级且复杂的方法。<u>如果您不完全了解设计选择的含义，则可能很容易遇到同步或计时问题，其严重性可能从细微的行为更改到应用程序崩溃以及用户数据损坏。</u></p>
<p>另一个要考虑的因素是你是否需要线程或并发。线程解决了如何在同一进程内同时执行多个代码路径的特定问题。<u>但是，在某些情况下，您所做的工作量并不能保证并发性。线程在内存消耗和 CPU 时间方面为您的进程带来了巨大的开销。您可能会发现此开销对于预期任务来说太大了，或者其他选项更容易实现。</u></p>
<p>表 1-1 列出了一些线程的替代方案。该表包括线程的替换技术（例如操作对象和 GCD）以及旨在有效使用您已有的单线程的替代方法。</p>
<p>表 1-1：线程的替代技术</p>
<table>
<thead>
<tr>
<th>技术</th>
<th style="text-align: center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>Operation objects</td>
<td style="text-align: center">在 OS X v10.5 中引入的操作对象是通常在辅助线程上执行的任务的包装器。<u>这个包装器隐藏了执行任务的线程管理方面，让您可以专注于任务本身。</u>您通常将这些对象与操作队列对象结合使用，该操作队列对象实际上管理一个或多个线程上的操作对象的执行。有关如何使用操作对象的更多信息，请参阅 <a href="https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html">Concurrency Programming Guide</a>。</td>
</tr>
<tr>
<td>Grand Central Dispatch (GCD)</td>
<td style="text-align: center">Grand Central Dispatch 在 Mac OS x v10.6 中引入，是线程的另一种替代方案，<u>可让您专注于执行所需的任务，而不是线程管理。</u>使用 GCD，您可以定义要执行的任务并将其添加到工作队列，该队列在适当的线程上处理任务的计划。工作队列考虑到可用内核的数量以及比使用线程本身更有效地执行任务的当前负载。有关如何使用 GCD 和工作队列的信息，请参阅 <a href="https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html">Concurrency Programming Guide</a>。</td>
</tr>
<tr>
<td>空闲时间通知</td>
<td style="text-align: center"><u>对于相对较短且优先级较低的任务，空闲时间通知允许您在应用程序不忙时执行任务。</u>Cocoa 使用 <code>NSNotificationQueue</code> 对象提供对空闲时间通知的支持。要请求空闲时间通知，请使用 <code>NSPostWhenIdle</code> 选项将通知发布到默认 <code>NSNotificationQueue</code> 对象。队列延迟通知对象的传递，直到运行循环变为空闲。有关更多信息，请参阅 <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Notifications/Introduction/introNotifications.html#//apple_ref/doc/uid/10000043-SW1">Notification Programming Topics</a>。</td>
</tr>
<tr>
<td>Asynchronous functions</td>
<td style="text-align: center">系统接口包括许多为您提供自动并发的异步函数。这些 API 可以使用系统守护程序和进程，或创建自定义线程来执行其任务并将结果返回给您。（实际的实现是无关紧要的，因为它与代码分开。）在设计应用程序时，查找提供异步行为的函数，并考虑使用它们而不是在自定义线程上使用等效的同步函数。</td>
</tr>
<tr>
<td>定时器</td>
<td style="text-align: center">您可以在应用程序的主线程上使用定时器来执行定期任务，<u>这些任务太简单，不需要线程，但仍需要定期维护。</u>有关定时器的信息，请参阅<a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW21">“定时器源”</a>。</td>
</tr>
<tr>
<td>分离进程</td>
<td style="text-align: center">尽管比线程更重要，<u>但在任务与应用程序只是切线相关的情况下，</u>创建单独的进程可能很有用。<u>如果任务需要大量内存或必须使用 root 权限执行，则可以使用进程。</u>例如，您可以使用 64 位服务器进程计算大型数据集，而 32 位应用程序将结果显示给用户。</td>
</tr>
</tbody>
</table>
<blockquote>
<p>警告：使用 <code>fork</code> 函数启动单独的进程时，必须始终通过调用 <code>exec</code> 或类似函数来调用 <code>fork</code>。依赖于 Core Foundation，Cocoa 或 Core Data 框架（显式或隐式）的应用程序必须对 <code>exec</code> 函数进行后续调用，否则这些框架可能表现不正常。</p>
</blockquote>
<h2><a id="%E7%BA%BF%E7%A8%8B%E6%94%AF%E6%8C%81" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>线程支持</h2>
<p>如果您有现有使用线程的代码，OS X 和 iOS 提供了几种在应用程序中创建线程的技术。此外，两个系统还支持管理和同步需要在这些线程上完成的工作。以下部分描述了在 OS X 和 iOS 中使用线程时需要注意的一些关键技术。</p>
<h3><a id="%E7%BA%BF%E7%A8%8B%E5%8C%85" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>线程包</h3>
<p>虽然线程的底层实现机制是 Mach 线程，但很少（如果有的话）使用 Mach 级别的线程。相反，您通常使用更方便的 POSIX API 或其衍生产品之一。然而，Mach 实现确实提供了所有线程的基本功能，包括抢先执行模型和调度线程的能力，因此它们彼此独立。</p>
<p>表 2-2 列出了可以在应用程序中使用的线程技术。</p>
<p>表 2-2：线程技术</p>
<table>
<thead>
<tr>
<th>技术</th>
<th style="text-align: center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cocoa 线程</td>
<td style="text-align: center">Cocoa 使用 <code>NSThread</code> 类实现线程。Cocoa 还提供了 <code>NSObject</code> 上的方法，用于生成新线程并在已经运行的线程上执行代码。有关更多信息，请参阅<a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/10000057i-CH15-SW11">使用 NSThread</a> 和<a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/10000057i-CH15-SW13">使用 NSObject 生成线程</a>。</td>
</tr>
<tr>
<td>POSIX 线程</td>
<td style="text-align: center">POSIX 线程提供了一个用于创建线程的基于 C 的接口。如果您没有编写 Cocoa 应用程序，这是创建线程的最佳选择。POSIX 接口使用起来相对简单，并为配置线程提供了充分的灵活性。有关更多信息，请参阅<a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/10000057i-CH15-SW12">使用 POSIX 线程</a>。</td>
</tr>
<tr>
<td>多处理服务</td>
<td style="text-align: center">多处理服务是从旧版 Mac OS 转换的应用程序使用的基于C的传统接口。此技术仅适用于 OS X，任何新开发都应避免使用。相反，您应该使用 NSThread 类或 POSIX 线程。如果需要有关此技术的更多信息，请参阅 <a href="https://developer.apple.com/library/archive/documentation/Carbon/Conceptual/Multitasking_MultiproServ/01introduction/introduction.html#//apple_ref/doc/uid/TP40000853">Multiprocessing Services Programming Guide</a>。</td>
</tr>
</tbody>
</table>
<p>在应用程序级别，所有线程的行为方式与其他平台上的行为基本相同。<u>启动线程后，线程以三种主要状态之一运行：running，ready 或 blocked。</u>如果一个线程当前没有运行，它将被阻塞并等待输入，或者它已准备好运行但尚未安排执行此操作。线程继续在这些状态之间来回移动，直到它最终退出并移动到终止状态。</p>
<p>创建新线程时，必须为该线程指定入口点函数（或 Cocoa 线程的入口点方法）。此入口点函数构成您要在线程上运行的代码。当函数返回时，或者显式终止线程时，线程将永久停止并由系统回收。由于线程在内存和时间方面的创建成本相对较高，因此建议您的入口点函数执行大量工作或设置运行循环以允许执行重复工作。</p>
<p>有关可用线程技术及其使用方法的更多信息，请参阅<a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/10000057i-CH15-SW2">线程管理</a>。</p>
<h3><a id="run-loops%EF%BC%88%E8%BF%90%E8%A1%8C%E5%BE%AA%E7%8E%AF%EF%BC%89" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>Run Loops（运行循环）</h3>
<p><u>运行循环是一个基础结构，用于管理在线程上异步到达的事件。运行循环通过监视线程的一个或多个事件源来工作。</u>当事件到达时，系统唤醒线程并将事件调度到运行循环，然后运行循环将它们分派给您指定的处理程序。如果没有事件存在且准备好处理，则运行循环使线程进入休眠状态。</p>
<p>您不需要对您创建的任何线程使用运行循环，但这样做可以为用户提供更好的体验。<u>运行循环可以创建使用最少量资源的长期线程。因为运行循环在没有任何操作时将其线程置于休眠状态，所以它消除了轮询的需要，这会浪费 CPU 周期并阻止处理器本身休眠并节省电力。</u></p>
<p>要配置运行循环，您所要做的就是启动线程，获取对运行循环对象的引用，安装事件处理程序，并告诉运行循环运行。OS X 提供的基础结构会自动为您处理主线程运行循环的配置。但是，如果您计划创建长期存在的辅助线程，则必须自己为这些线程配置运行循环。</p>
<p><a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW1">Run Loops</a> 中提供了有关运行循环的详细信息以及如何使用它们的示例。</p>
<h3><a id="%E5%90%8C%E6%AD%A5%E5%B7%A5%E5%85%B7" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>同步工具</h3>
<p>线程编程的一个危险是多线程之间的资源争用。如果多个线程尝试同时使用或修改同一资源，则可能会出现问题。缓解该问题的一种方法是完全消除共享资源，并确保每个线程都有自己独特的资源集来操作。但是，维护完全独立的资源不是一种选择，<u>您可能必须使用锁，条件，原子操作和其他技术来同步对资源的访问。</u></p>
<p>锁为代码提供强力形式的保护，一次只能由一个线程执行。最常见的锁类型是互斥锁，也称为互斥锁。当一个线程试图获取当前由另一个线程持有的互斥锁时，它会阻塞，直到另一个线程释放该锁。多个系统框架提供对互斥锁的支持，尽管它们都基于相同的底层技术。此外，Cocoa 提供了互斥锁的几种变体，以支持不同类型的行为，例如递归。有关可用锁类型的更多信息，请参阅<a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8-126320">锁</a>。</p>
<p>除锁外，系统还提供对条件的支持，以确保应用程序中任务的正确排序。条件充当网守，阻止给定线程，直到它表示的条件变为真。当发生这种情况时，条件释放线程并允许它继续。POSIX 层和 Foundation 框架都为条件提供直接支持。（如果使用操作对象，则可以配置操作对象之间的依赖关系以对任务的执行进行排序，这与条件提供的行为非常相似。）</p>
<p>虽然锁和条件在并发设计中非常常见，但原子操作是保护和同步数据访问的另一种方法。在可以对标量数据类型执行数学或逻辑运算的情况下，原子操作为锁提供了轻量级替代。原子操作使用特殊的硬件指令来确保在其他线程有机会访问变量之前完成对变量的修改。</p>
<p>有关可用同步工具的详细信息，请参阅<a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8-124887">同步工具</a>。</p>
<h3><a id="%E7%BA%BF%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>线程间通信</h3>
<p>虽然良好的设计可以最大限度地减少所需的通信量，但在某些时候，线程之间的通信变得必要。（线程的工作是为您的应用程序工作，但如果从未使用过该作业的结果，它有什么用处？）线程可能需要处理新的作业请求或将其进度报告给应用程序的主线程。在这些情况下，您需要一种方法来从一个线程获取信息到另一个线程。幸运的是，线程共享相同的进程空间这一事实意味着您有很多通信选项。</p>
<p>线程之间有许多通信方式，每种方式都有自己的优点和缺点。配置线程局部存储列出了可以在 OS X 中使用的最常见的通信机制。（除了消息队列和 Cocoa 分布式对象，这些技术也可以在 iOS 中使用。）表 1-3 中的技术按增加的复杂程度顺序列出。</p>
<p>表 1-3：传递机制</p>
<table>
<thead>
<tr>
<th>机制</th>
<th style="text-align: center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>直接消息传递</td>
<td style="text-align: center"><u>Cocoa 应用程序支持直接在其他线程上执行选择器的能力。</u>此功能意味着一个线程基本上可以在任何其他线程上执行方法。因为它们是在目标线程的上下文中执行的，所以以这种方式发送的消息会在该线程上自动序列化。有关输入源的信息，请参阅<a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW44">Cocoa 执行选择器源</a>。</td>
</tr>
<tr>
<td>全局变量，共享内存和对象</td>
<td style="text-align: center">在两个线程之间传递信息的另一种简单方法是使用全局变量，共享对象或共享内存块。虽然共享变量快速而简单，但它们也比直接消息传递更脆弱。必须使用锁或其他同步机制小心保护共享变量，以确保代码的正确性。如果不这样做可能会导致竞争条件，数据损坏或崩溃。</td>
</tr>
<tr>
<td>条件</td>
<td style="text-align: center">条件是一种同步工具，<u>可用于控制线程何时执行特定代码部分。</u>您可以将条件视为门卫，让线程仅在满足所述条件时运行。有关如何使用条件的信息，请参阅<a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8-SW4">使用条件</a>。</td>
</tr>
<tr>
<td>运行循环源</td>
<td style="text-align: center">自定义运行循环源是您设置为在线程上接收特定于应用程序的消息的源。因为它们是事件驱动的，所以当没有任何事情要做时，运行循环源会让你的线程自动进入休眠状态，从而提高线程的效率。有关运行循环和运行循环源的信息，请参阅 <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8-SW4">Run Loops</a>。</td>
</tr>
<tr>
<td>端口和 sockets（套接字）</td>
<td style="text-align: center">基于端口的通信是两种线程之间通信的更精细的方式，但它也是一种非常可靠的技术。<u>更重要的是，端口和套接字可用于与外部实体（例如其他进程和服务）进行通信。</u>为了提高效率，端口是使用运行循环源实现的，因此当端口上没有数据等待时，线程会休眠。有关运行循环和基于端口的输入源的信息，请参阅 <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8-SW4">Run Loops</a>。</td>
</tr>
<tr>
<td>消息队列</td>
<td style="text-align: center">传统的多处理服务定义了用于管理传入和传出数据的先进先出（FIFO）队列抽象。尽管消息队列简单方便，但它们并不像其他一些通信技术那样高效。有关如何使用消息队列的详细信息，请参阅 <a href="https://developer.apple.com/library/archive/documentation/Carbon/Conceptual/Multitasking_MultiproServ/01introduction/introduction.html#//apple_ref/doc/uid/TP40000853">Multiprocessing Services Programming Guide</a>。</td>
</tr>
<tr>
<td>Cocoa 分布式对象</td>
<td style="text-align: center">分布式对象是一种 Cocoa 技术，可提供基于端口的通信的高级实现。尽管可以将此技术用于线程间通信，<u>但由于其产生的开销量很大，因此非常不鼓励这样做。</u>分布式对象更适合与其他进程通信，其中进程之间的开销已经很高。有关更多信息，请参阅 <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/DistrObjects/DistrObjects.html#//apple_ref/doc/uid/10000102i">Distributed Objects Programming Topics</a>。</td>
</tr>
</tbody>
</table>
<h2><a id="%E8%AE%BE%E8%AE%A1%E6%8A%80%E5%B7%A7" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>设计技巧</h2>
<p>以下部分提供了一些指导原则，可帮助您以确保代码正确性的方式实现线程。其中一些指南还提供了使用您自己的线程代码实现更好性能的提示。与任何性能提示一样，您应始终在更改代码之前，期间和之后收集相关的性能统计信息。</p>
<h3><a id="%E9%81%BF%E5%85%8D%E6%89%8B%E5%8A%A8%E5%9C%B0%E5%88%9B%E5%BB%BA%E7%BA%BF%E7%A8%8B" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>避免手动地创建线程</h3>
<p><u>手动编写线程创建代码很繁琐且可能容易出错，您应尽可能避免使用它。</u>OS X 和 iOS 通过其他 API 提供对并发的隐式支持。不要自己创建一个线程，而是考虑使用异步 API，GCD 或操作对象来完成工作。这些技术为您做幕后的线程相关工作，并保证正确执行。此外，通过根据当前系统负载调整活动线程数，GCD 和操作对象等技术可以比您自己的代码更有效地管理线程。有关 GCD 和操作对象的更多信息，请参阅 <a href="https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008091">Concurrency Programming Guide</a></p>
<h3><a id="%E4%BF%9D%E6%8C%81%E4%BD%A0%E7%9A%84%E7%BA%BF%E7%A8%8B%E5%90%88%E7%90%86%E7%B9%81%E5%BF%99" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>保持你的线程合理繁忙</h3>
<p>如果您决定手动创建和管理线程，请记住线程占用宝贵的系统资源。您应该尽力确保为线程分配的任何任务都是合理的长寿和高效的。与此同时，您不应该害怕终止花费大部分时间闲置的线程。<u>线程使用大量内存，其中一些是有线的，因此释放空闲线程不仅有助于减少应用程序的内存占用，还可以释放更多物理内存供其他系统进程使用。</u></p>
<blockquote>
<p>要点：在开始终止空闲线程之前，应始终记录应用程序当前性能的一组基线度量。尝试更改后，请进行其他测量以验证更改是否实际上提高了性能，而不是伤害它。</p>
</blockquote>
<h3><a id="%E9%81%BF%E5%85%8D%E4%BD%BF%E7%94%A8%E5%85%B1%E4%BA%AB%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>避免使用共享数据结构</h3>
<p>避免与线程相关的资源冲突的最简单和最容易的方法是为程序中的每个线程提供它所需的任何数据的副本。当您最小化线程之间的通信和资源争用时，并行代码最有效。</p>
<p>创建多线程应用程序很难。即使您非常小心并在代码中的所有正确接合点锁定共享数据结构，您的代码仍可能在语义上不安全。例如，如果希望以特定顺序修改共享数据结构，则代码可能会遇到问题。将代码更改为基于事务的模型以进行补偿可能随后会抵消具有多个线程的性能优势。首先消除资源争用通常会导致设计更简单，性能更佳。</p>
<h3><a id="%E7%BA%BF%E7%A8%8B%E5%92%8C%E6%82%A8%E7%9A%84%E7%94%A8%E6%88%B7%E7%95%8C%E9%9D%A2" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>线程和您的用户界面</h3>
<p>如果您的应用程序具有图形用户界面，建议您从应用程序的主线程接收与用户相关的事件并启动界面更新。此方法有助于避免与处理用户事件和绘制窗口内容相关的同步问题。某些框架（如 Cocoa）通常需要此行为，但即使对于那些不这样做的框架，将此行为保留在主线程上也具有简化管理用户界面的逻辑的优势。</p>
<p>有一些值得注意的例外情况，从其他线程执行图形操作是有利的。例如，您可以使用辅助线程来创建和处理图像，并执行其他与图像相关的计算。使用辅助线程进行这些操作可以大大提高性能。如果您不确定特定的图形操作，请计划从主线程执行此操作。</p>
<p>有关 Cocoa 线程安全性的更多信息，请参阅<a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-SW1">线程安全摘要</a>。有关使用 Cocoa 绘图的更多信息，请参阅 <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40003290">Cocoa Drawing Guide</a>。</p>
<h3><a id="%E5%9C%A8%E9%80%80%E5%87%BA%E6%97%B6%E6%84%8F%E8%AF%86%E5%88%B0%E7%BA%BF%E7%A8%8B%E8%A1%8C%E4%B8%BA" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>在退出时意识到线程行为</h3>
<p>进程将一直运行，直到所有未分离的线程都退出。默认情况下，只将应用程序的主线程创建为非分离，但您也可以创建其他线程。当用户退出应用程序时，通常认为立即终止所有分离的线程是合适的行为，因为分离线程完成的工作被认为是可选的。但是，如果您的应用程序使用后台线程将数据保存到磁盘或执行其他关键工作，您可能希望将这些线程创建为非分离，以防止在应用程序退出时丢失数据。</p>
<p>将线程创建为非分离（也称为可连接）需要您做额外的工作。由于大多数高级线程技术默认情况下不创建可连接线程，因此您可能必须使用POSIX API来创建线程。此外，您必须在应用程序的主线程中添加代码，以便在最终退出时与非分离线程连接。有关创建可连接线程的信息，请参阅<a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/10000057i-CH15-SW3">设置线程的已分离状态</a>。</p>
<p>如果您正在编写 Cocoa 应用程序，您还可以使用 <code>applicationShouldTerminate:</code> 代理方法将应用程序的终止延迟到以后的时间或完全取消它。延迟终止时，您的应用程序需要等待任何关键线程完成其任务，然后调用 <code>replyToApplicationShouldTerminate:</code> 方法。有关这些方法的详细信息，请参阅 <a href="https://developer.apple.com/documentation/appkit/nsapplication">NSApplication Class Reference</a>。</p>
<h3><a id="%E5%A4%84%E7%90%86%E5%BC%82%E5%B8%B8" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>处理异常</h3>
<p>异常处理机制依赖于当前调用堆栈在抛出异常时执行任何必要的清理。<u>因为每个线程都有自己的调用堆栈，所以每个线程都负责捕获自己的异常。</u>未能在辅助线程中捕获异常与未在主线程中捕获异常相同：拥有的进程终止。您不能将未捕获的异常抛出到其他线程进行处理。</p>
<p>如果您需要通知当前线程中异常情况的另一个线程（例如主线程），您应该捕获异常并简单地向另一个线程发送消息，指示发生了什么。根据您的模型和您要执行的操作，捕获异常的线程可以继续处理（如果可能），等待指令，或者只是退出。</p>
<blockquote>
<p>注意：在 Cocoa 中，<code>NSException</code> 对象是一个自包含的对象，一旦被捕获就可以从一个线程传递给另一个线程。</p>
</blockquote>
<p>在某些情况下，可能会自动为您创建异常处理程序。例如，objectivec中的 <code>@synchronized</code> 指令包含一个隐式异常处理程序。</p>
<h3><a id="%E5%B9%B2%E5%87%80%E5%9C%B0%E7%BB%88%E6%AD%A2%E6%82%A8%E7%9A%84%E7%BA%BF%E7%A8%8B" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>干净地终止您的线程</h3>
<p>线程退出的最佳方法当然是让它到达主入口点例程的末尾。<u>虽然有立即终止线程的函数，但这些函数应该仅作为最后的手段使用。</u>在线程到达其自然终点之前终止线程会阻止线程自行清理。如果线程已分配内存，打开文件或获取其他类型的资源，则代码可能无法回收这些资源，从而导致内存泄漏或其他潜在问题。</p>
<p>有关退出线程的正确方法的更多信息，请参阅<a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/10000057i-CH15-SW10">终止线程</a>。</p>
<h3><a id="%E5%BA%93%E5%86%85%E7%9A%84%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>库内的线程安全</h3>
<p>虽然应用程序开发人员可以控制应用程序是否使用多个线程执行，但库开发人员却没有。<u>在开发库时，您必须假设调用应用程序是多线程的，或者可以随时切换到多线程。</u>因此，您应始终对关键的代码段使用锁。</p>
<p>对于库开发人员，仅在应用程序变为多线程时才创建锁是不明智的。如果您需要在某个时刻锁定代码，请在使用库的早期创建锁定对象，最好是在某种显式调用中初始化库。虽然您也可以使用静态库初始化函数来创建此类锁，但只有在没有其他方法时才尝试这样做。执行初始化函数会增加加载库所需的时间，并可能对性能产生负面影响。</p>
<blockquote>
<p>注意：始终记得平衡调用以锁定和解锁库中的互斥锁。您还应该记住锁定库数据结构，而不是依赖调用代码来提供线程安全的环境。</p>
</blockquote>
<p>如果您正在开发 Cocoa 库，那么如果您希望在应用程序变为多线程时收到通知，则可以注册为 <code>NSWillBecomeMultiThreadedNotification</code> 的观察者。但是，您不应该依赖于接收此通知，因为它可能会在调用库代码之前调用。</p>
<div style="padding-top:25px;">
    <div style="float:right">
        <a style="text-decoration:none" href="%E7%BA%BF%E7%A8%8B%E7%AE%A1%E7%90%86.html">下一章：线程管理 &raquo;</a> 
    </div>
</div>

                  </article>
                  <div class="comments-wrap">
                    <div class="share-comments">
                      

                      

                      
                    </div>
                  </div><!-- end comments wrap -->
              </div>
            </div><!-- end columns -->
      </div><!-- end container -->
    </section>



    <footer class="footer">
        <div class="content has-text-centered">
          <p>
              Copyright &copy; 2019
              Powered by <a target="_blank" href="http://www.mweb.im">MWeb</a>,&nbsp; 
              Theme used <a target="_blank" href="https://bulma.io/">Bulma CSS</a>.
          </p>
        </div>
      </footer>



  















  
    




  </body>
</html>
